#ifndef ITEMSET_H
#define ITEMSET_H

#include <list>
#include <iostream>

using namespace std;

/**
 * In this namespace all classes included, which are implementing the 
 * apriori algorithm.
 * \version 0.1
 * \author Michael Opitz
 */
namespace apriori {
	template <class transaction,
		  template <class> class collection
		 >
	/**
	 * This class represents an itemset.
	 * Template parameters \see apriori_alg but they should be created
	 * from apriori_alg anyway...
	 */
	class itemset {
	public:
		// we'll need a few types...
		typedef typename transaction::data data;
		typedef typename transaction::data_ptr data_ptr;
		typedef typename transaction::data_coll data_coll; 
		typedef typename transaction::ptr trans_ptr;
		typedef collection<trans_ptr> t_trans_coll;
		// note: data_coll must consist of data_ptrs

		//! Standard constructor
		itemset():_support(0), _confidence(0.0f) {}
		//! Constructor which initializes the internal data with tmp
		itemset(const data_coll &tmp):_support(0), _confidence(0.0f) 
		{set_data(tmp);}
		~itemset() {}

		//! Set-method for items. Normally you shouldn't use this!
		void set_data(const data_coll &tmp)
		{
			_items.clear();
			typedef typename data_coll::const_iterator it;
			for(it i = tmp.begin(); i != tmp.end(); ++i)
			{
				_items.push_back(*i);
			}
		}

		//! returns a list of the items
		list<data_ptr>& get_items() {return _items;}
		//! returns a const list of the items
		const list<data_ptr>& get_items() const {return _items;}
		//! returns the support value of the itemset
		int get_support() const { return _support; }
		//! returns the confidence
		float get_confidence() const { return _confidence; }

		//! checks the support value.
		/**
		 * Calculates the own support value with the transactions col
		 * \col is a collection of transactions
		 */
		void check_support(const t_trans_coll &col)
		{
			int oldsupport = _support;
			_support = 0;
			typedef typename t_trans_coll::const_iterator it;
			for(it i = col.begin(); i != col.end(); ++i)
			{
				const data_coll &col = (*i)->get_elements();
				check_support_data(_items, col);
			}
			// now check the confidence
			if(_support != 0 && oldsupport != 0) {
				_confidence = float(_support) / 
					float(oldsupport);
			} else {
				// if the support is/was 0, confidence is 
				// set to infinity
				_confidence = 1.0f/0.0f;
			}
		}

		//! Simple != implementation
		bool operator != (const itemset &set) const
		{
			return !(this->operator == (set));
		}

		//! Simple == implementation
		bool operator == (const itemset &set) const
		{
			typedef typename list<data_ptr>::const_iterator it;
			const list<data_ptr> &tmp = set.get_items();
			if(tmp.size() != _items.size()) {
				return false;
			}
			// all elements must match
			for(it i = _items.begin(); i != _items.end(); ++i) {
				bool found = false;
				for(it j = tmp.begin(); j != tmp.end(); ++j) {
					if(*j == *i) {	// only compare by ref.
						found = true;
					}
				}
				if(!found) {
					return false;
				}
			}
			return true;
		}
		
		/**
		 * Tries to find ptr in _items
		 * \return true is returned, if ptr was found in _items
		 */
		bool find(const data_ptr &ptr) const
		{
			typedef typename list<data_ptr>::const_iterator it;
			for(it i = _items.begin(); i != _items.end(); ++i) {
				if((*i) == ptr) {
					return true;
				}
			}
			return false;
		}

		template <class T>
		/**
		 * Writes the contents of the collection to the stream t.
		 * T must implement the <<-operator
		 */
		void write(T &t) const
		{
			typedef typename list<data_ptr>::const_iterator it;
			for(it i = _items.begin(); i != _items.end(); ++i) {
				t << (**i) << " ";
			}
		}
	private:
		/**
		 * Calculates the own support value with the data_coll-row from 
		 * a transaction.
		 */
		void check_support_data(list<data_ptr> &l, const data_coll &col)
		{
			typedef typename list<data_ptr>::iterator it;
			typedef typename data_coll::const_iterator it2;
			for(it i = l.begin(); i != l.end(); ++i) {
				bool found = false;
				for(it2 j = col.begin(); j != col.end(); ++j) {
					if(**j == **i) {
						found = true;
						break;
					}
				}
				if(!found) {
					// no support, because not all items
					// are included in transaction
					return;
				}
			}
			_support++;
		}

		list<data_ptr> _items;
		int _support;
		float _confidence;
	};
}

template <class transaction,
	  template <class> class collection
	 >
/**
 * Implementation of the ostream-operator for cout
 */
ostream& operator << (ostream &os, 
		const apriori::itemset<transaction, collection> &it)
{
	it.write(os);
	return os;
}

#endif
